#ifndef WORLD_H_
#define WORLD_H_

#include "Color.h"
#include "Sphere.h"
#include "Ray.h"
#include "ContactData.h"
#include "EventHandler.h"
#include <vector>
#include "Task.h"


/* Forward declarations */
class RayTracer;
class Renderable;
class Camera;
class Thread;
class Light;
class Grid;
class Texture;
class CriticalSection;
struct MainRenderStateData;
typedef std::vector<Renderable*> vRenderables;


/************************************************************************/
/*  A scene stores all the data necessary for a specific type of target */
/*  image. The RayTracer* may change to have a different effect on      */
/*  the scene                                                           */
/************************************************************************/

class Scene : public EventHandler
{	
    /* Frequently Accessed Data */
public:
    int                         m_resX, m_resY; // the resolution of the samples  
    float                       m_pixelSize;    // the size of each sample
    Color					    m_clearColor;   // the default color of the samples
    RayTracer*				    m_rayTracer;    // the main ray tracing function
    Sphere 				        m_sphere;       // simple sphere for debugging purposes only
    vRenderables                m_renderableObjects;    // all the render able objects
    Camera*                     m_camera;
    Grid*                       m_grid;
    Texture*                    m_triTex;

public:
    /* Main Ctr */
    Scene();
    ~Scene();
    void CleanUp();
    Scene(int test){}
    /* Ads a renderable object to this Scene */
    void AddRenderable(Renderable* object_ptr);
    /* Builds this Scene */
    void Build(MainRenderStateData& data);
    void BuildSimpleProcedrual(MainRenderStateData& data);
    void BuildComplexProcedrual(MainRenderStateData& data);
    void UseSimpleMesh(MainRenderStateData& data);
    void UseCityMesh(MainRenderStateData& data);
    void SubdivideTaskList();
    /* Renders this Scene */
    void RenderSingleThreaded();
    void RenderMultipleThreaded();

    void ClearBuffers();
    void ResetCamera();
    std::vector<Light*>& GetLights();
    void RenderWithFourThreads();
    void RenderWithSingleThread();
    void RenderWithTwoThreads();
    void RenderWithNThreads();
    vRenderables& GetRenderables();
    /* Platform independant function */
    void SetPixelColor(const int row, const int column, const Color& pixel_color);
    bool TrySetDepth(int row, int column, float dist);
    /* OpenGL specific implementation of rendering */
    void GLSetPixel(int x, int y, float r, float g, float b);
    void GLRenderPixels();
    
    /* To be defined later */
    ContactData CheckContact(const Ray& ray,bool nearest = true);
    virtual void OnKeyBoardDownEvent(const SDL_KeyboardEvent&);

    //finished with TASKS
    void ThreadFinished();
    //ENDING application
    void ThreadEnded();
    bool GetTask(Ray_Trace_Task& t);
    void SetTimer(int which, double val);
    double* GetTimer(int which){return &m_taskTimers[which];}
    Vec3 m_cameraMove;
#ifdef DEBUGGING
        virtual void OnMouseButton(const SDL_MouseButtonEvent& Event);
        ContactData CheckContact_Debug(const Ray& ray,bool nearest = true);
#endif
    /* Non Public data / functions */
private:
    /* To be called at program terminate */
    void CheckContactsGrid( ContactData& ret , const Ray& ray,bool nearest);
    void CheckContactsBruteForce( ContactData& ret, const Ray& ray,bool nearest );

    void TidyMutexs();

    /* The color buffer to be sent to the screen */
    float* m_mainArray;
    float* m_depthBuffer;
    //for synch
    CriticalSection* m_threadFinishCS;
    CriticalSection* m_threadTaskCS;
    CriticalSection* m_threadEndCS;
    int m_numTasksLeft;
    std::vector<Ray_Trace_Task> m_tasks;
    std::vector<Light*> m_lights;
    //depth or color
    bool m_renderDepth;
    Thread* m_threads[8];
    int m_threadFinishCounter;
    int m_threadEndCounter;
    void* m_finishEvent;
    void* m_endEvent;
    int m_numThreads;
    Vec3 m_cameraStart;
    double m_taskTimers[8];
    
};

inline void Scene::AddRenderable(Renderable* object_ptr) 
{  
    m_renderableObjects.push_back(object_ptr);	
}

#endif
